﻿package org.hive.managers.plugin
{
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.net.URLRequest;
	import flash.utils.Dictionary;
	import org.hive.data.LoaderData;
	import org.hive.managers.StateManager;
	import org.hive.sequence.Sequence;
	import org.hive.managers.plugin.LoadManager;
	import org.hive.managers.Manager;
	import org.hive.media.VideoSWF;
	import org.hive.events.VideoSWFEvent;
	import org.hive.events.VideoManagerEvent;
	
	import org.hive.utils.KeyframeAlbum;
	
	public class VideoSWFManager extends Manager
	{
		public static var currentVideo 			: VideoSWF;
		public static var isPaused 				: Boolean = false;
		public static var isScrubable 			: Boolean = true;
		public static var isScrubing 			: Boolean = false;
		public static var bufferTolerance 		: Number = 10;
		
		private static var __VIDEOS				: Dictionary = new Dictionary( true );
		private static var __VIDEOS_BY_NAME		: Dictionary = new Dictionary( true );
		
		
		public static var initialized:Boolean = false;
		
		public static function init():void
		{
			if( !initialized ) {
				trace( "VideoSWFManager :: initializing" );
				
				//_____________ Register Object :: Animator
				function videoSWFFunc( action:*, sequence:Sequence, positionInSequence:int ):void {
					if( sequence.getParams( action ) != null){
						LayoutManager.addChildAt( action, sequence.getParams( action )[0] );
					} else LayoutManager.addChild( action );
					VideoSWFManager.playVideo( action, LoadManager.getLoaderByName( VideoSWFManager.getVideoName(action)).name );
				}
				StateManager.registerAction(VideoSWF, videoSWFFunc);
				
				
				// Loader Element Distribution
				if ( !LoadManager.initialized ) LoadManager.init();
				function loaderElementFunction( loaderElement:* ):void {
					if ( loaderElement.contentLoaderInfo.content as VideoSWF ) {
						VideoSWFManager.add( loaderElement );
					} 	
				}
				
				LoadManager.registerLoaderElement(LoaderData, loaderElementFunction);
				
				initialized = true;
			}
		}
		
		
		// MANAGEMENT
		public static function add( ...args ):void
		{
			for (var i:Number = 0; i < args.length; i++) {
				__VIDEOS_BY_NAME[ args[i].name ] = args[i].contentLoaderInfo.content;
				__VIDEOS[ args[i].contentLoaderInfo.content ] = args[i].name;
		    }
		}
		
		public static function remove( video:* ):void
		{
			delete __VIDEOS[ video ];
			delete __VIDEOS_BY_NAME[ video.name ];
		}
		
		public static function removeByName( ...names ):void
		{
			for (var i:Number = 0; i < names.length; i++) {
		        remove( getVideoByName( names[i] ) )
		    }
		}
		
		public static function getVideoByName( name:String ):VideoSWF
		{
			return __VIDEOS_BY_NAME[ name ];
		}
		
		public static function getVideoName( obj:VideoSWF ):String
		{
			return __VIDEOS[ obj ];
		}
		
		
		
		
		
		//CONTROLS
		public static function playVideo( video:DisplayObject, name:String ): void
		{
			video.addEventListener( VideoSWFEvent.ON_LABEL, handleLabel, false, 0, true );
			video.addEventListener( Event.ENTER_FRAME, handleEnterFrame, false, 0, true );
			
			if( isBuffering ) stopBuffer();
			
			var previousVideo : VideoSWF = currentVideo as VideoSWF;
			currentVideo = video as VideoSWF;
			currentVideo.gotoAndStop( currentVideo.getLabelByName("Video_start").frame);
			resume();

			dispatchEvent( new VideoManagerEvent( VideoManagerEvent.ON_VIDEO_SWITCHED, currentVideo, name, previousVideo ) );
		}
		
		public static function kill( video:DisplayObject ):void
		{
			var v:VideoSWF = video as VideoSWF;
			v.removeEventListener( VideoSWFEvent.ON_LABEL, handleLabel );
			v.removeEventListener( Event.ENTER_FRAME, handleEnterFrame );
		}
		
		public static function pause(): void
		{
			currentVideo.stop();
			dispatchEvent( new VideoManagerEvent( VideoManagerEvent.ON_VIDEO_PAUSED, currentVideo, currentVideo.name, null, currentVideo.currentLabel ) );
			isPaused = true;
		}
		
		public static function resume(): void
		{
			var progress:Number = currentVideo.currentFrame/currentVideo.totalFrames;
			currentVideo.play();
			dispatchEvent( new VideoManagerEvent( VideoManagerEvent.ON_VIDEO_RESUMED, currentVideo, currentVideo.name, null, currentVideo.currentLabel, progress ) );
			isPaused = false;
		}
		
		public static function scrubVideo( percent:Number ):void
		{
			var position:Number = percent*currentVideo.totalFrames;
			currentVideo.gotoAndStop( Math.round(position) );
		}
		
		public static function scrubVideoToLabel( frame:Number ):void
		{
			pause();
			currentVideo.gotoAndStop( frame );
			resume();
		}
		
		private static var __videoProgress 			: Number;
		private static var __videoSecondsElapsed 	: Number;
		
		public static function get videoProgress():Number
		{
			return __videoProgress;
		}
		
		public static function frameIsLoaded( frame:Number ):Boolean
		{
			var loaded:Boolean;
			if( frame > currentVideo.framesLoaded ) loaded = false;
			else loaded = true;
			return loaded;
		}
		
		
		
		//Buffering
		private static var __bufferToFrame 	: Number;
		private static var __isBuffering 	: Boolean = false;
		public static function get isBuffering():Boolean { return __isBuffering; }
		
		public static function buffer( frame:Number ):void
		{
			__bufferToFrame = frame;
			currentVideo.removeEventListener( Event.ENTER_FRAME, handleEnterFrame );
			currentVideo.addEventListener( Event.ENTER_FRAME, handleBuffering, false, 0, true );
			dispatchEvent( new VideoManagerEvent( VideoManagerEvent.ON_VIDEO_BUFFERING, currentVideo, currentVideo.name ) );
			__isBuffering = true;
			pause();
		}
		
		public static function stopBuffer():void
		{
			dispatchEvent( new VideoManagerEvent( VideoManagerEvent.ON_VIDEO_BUFFER_DONE, currentVideo, currentVideo.name ) );
			currentVideo.removeEventListener( Event.ENTER_FRAME, handleBuffering );
			currentVideo.addEventListener( Event.ENTER_FRAME, handleEnterFrame, false, 0, true );
			__isBuffering = false;
			resume();
		}
		
		
		
		
		
		
		//EVENTS
		public static function handleEnterFrame( e:Event ):void
		{
			/*
			var fps:Number = e.currentTarget.stage == null ? e.currentTarget.parent.frameRate : e.currentTarget.stage.frameRate;
			
			__videoProgress = (e.currentTarget.currentFrame / e.currentTarget.totalFrames)*100;
			__videoSecondsElapsed = e.currentTarget.currentFrame / 30; //30 = FPS
			e.currentTarget.dispatchEvent( new VideoSWFEvent( VideoSWFEvent.ON_PROGRESS, e.currentTarget.name, "progress", __videoProgress, __videoSecondsElapsed ) );
			*/
			
			dispatchEvent( new VideoManagerEvent( VideoManagerEvent.ON_VIDEO_PROGRESS, e.currentTarget, e.currentTarget.name ) );
			
			if( (e.currentTarget.framesLoaded - e.currentTarget.currentFrame) < bufferTolerance && (e.currentTarget.totalFrames - e.currentTarget.currentFrame) > bufferTolerance && !isScrubing ){
				if( (e.currentTarget.totalFrames - e.currentTarget.currentFrame) < 300 ) buffer( e.currentTarget.totalFrames );
				else buffer( e.currentTarget.currentFrame + 300 );
			}
		}
		
		private static function handleBuffering( e:Event ):void
		{
			if( frameIsLoaded( __bufferToFrame ) ){
				stopBuffer();
			}
		}
		
		public static function handleLabel( e:VideoSWFEvent ):void
		{
			dispatchEvent( new VideoManagerEvent( VideoManagerEvent.ON_VIDEO_LABEL, currentVideo, e.name, null, e.state ) );
		}
		
		
		//_________________________________________________________________________________________________ STATIC EVENT DISPATCHER METHODS
		protected static var disp:EventDispatcher;
		
		public static function addEventListener(p_type:String, p_listener:Function, p_useCapture:Boolean = false, p_priority:int = 0, p_useWeakReference:Boolean = false):void {
			if (disp == null) { disp = new EventDispatcher(); }
			disp.addEventListener(p_type, p_listener, p_useCapture, p_priority, p_useWeakReference);
		}
		
		public static function removeEventListener(p_type:String, p_listener:Function, p_useCapture:Boolean=false):void {
			if (disp == null) { return; }
			disp.removeEventListener(p_type, p_listener, p_useCapture);
		}
		
		public static function dispatchEvent(p_event:Event):void {
			if (disp == null) { return; }
			disp.dispatchEvent(p_event);
		}
			
	}
}